#!/usr/bin/env python
# -*- coding:utf-8 -*-
"""
@author: xjx
@time: 2023/11/5 21:35 
@file: fuel_predict.py
@project: nanchangproject
@describe: TODO
"""
import sys
import os
import time
import django
from django.db.models import QuerySet
import joblib
from concurrent.futures import ThreadPoolExecutor, as_completed, ProcessPoolExecutor

mlp_model = joblib.load('saved_model_mlp.pkl')  # 全局变量，可以使用，但是没用
sys.path.append('../../')
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'nanchangproject.settings')
django.setup()  # 很关键的配置，必须先setup在导入models类

from drivinginfo.models import Drivinginfo


def mlp_multithreading(query: QuerySet, pool_num: int = 10, update_num: int = 10):
    """
    :param pool_num: 建立进程个数
    :param query: 一个未进行切片的完整queryset对象
    :param update_num: 需要更新的数据量，测试不需要全部数据更新
    :return: None
    """
    pid = os.getpid()
    print(f'进程id:{pid}开启执行')
    query_len = len(query)  # 计算数据量
    start_num = query.first().id  # 取第一条数据的id值
    end_num = start_num + update_num
    with ThreadPoolExecutor(max_workers=pool_num) as pool:
        for line in query:
            id_num = line.id
            pool.submit(mlp_callback, id_num, query)
            if id_num == end_num:
                break
    print(f'进程{pid}的所有线程执行完毕')


def mlp_callback(id_num: int, query: QuerySet[Drivinginfo]):
    """
    :param id_num: 当前处理的数据id
    :param query: 一个未进行切片的完整queryset对象
    :return: dict类型预测结果
    """
    sub_query = query.filter(id=id_num)  # 根据id筛选，别加first，会转化为Carinfo对象
    sub_query_value = sub_query.values_list('car_speed', 'atmospheric_pressure',
                                            'actual_torque',
                                            'friction_torque', 'engine_speed',
                                            'fuel_flow', 'reactive_agent',
                                            'air_intake',
                                            'scr_inlet_temperature',
                                            'scr_outlet_temperature',
                                            'dpf_pressure_difference',
                                            'coolant_temperature', 'tank_level')  # 筛选值
    for num in sub_query_value:
        if num == -10000:
            print('数据异常')
            return None
    x = list(sub_query_value)  # queryset转化为二维列表
    global mlp_model  # 使用全局变量的速度要快很多
    try:
        result = mlp_model.predict(x)[0]  # 返回结果是一个列表，因此必须切片
        result = round(result, 4)
        sub_query.update(fuel_consumption_pre=result)
        print(f'{id_num}的预测结果为{result}数据更新成功')
    except Exception as e:
        print(e, '输入数据错误')


def mlp_predict():
    global mlp_model
    queryset = Drivinginfo.objects.filter(fuel_consumption_pre__isnull=True)
    count = queryset.count()  # 计算数据量
    if count == 0:
        print('无需要更新的数据')
        return
    for item in queryset:
        result = 0
        if item.engine_status == '关闭':
            # 发动机关闭油耗预测为0才对
            item.fuel_consumption_pre = 0
        else:
            x = [[item.car_speed, item.atmospheric_pressure,
                  item.actual_torque, item.friction_torque,
                  item.engine_speed, item.fuel_flow, item.reactive_agent,
                  item.air_intake, item.scr_inlet_temperature, item.scr_outlet_temperature,
                  item.dpf_pressure_difference, item.coolant_temperature,
                  item.tank_level]]
            valid_flag = True
            for num in x[0]:
                if num == -10000:
                    # 无效数据
                    item.fuel_consumption_pre = 0
                    valid_flag = False
                    break
            if valid_flag:
                result = mlp_model.predict(x)[0]
                result = round(result, 4)
                if result <= 0:
                    item.fuel_consumption_pre = 0
                    result = 0
                else:
                    item.fuel_consumption_pre = result
        print(f'{item.id}数据预测结果为{result}')
    Drivinginfo.objects.bulk_update(queryset, ['fuel_consumption_pre'], batch_size=10000)


if __name__ == '__main__':
    # my_query = Drivinginfo.objects.filter(fuel_consumption_pre__isnull=True)  # 查找所有预测值为空的query
    start_time = time.perf_counter()
    # mlp_multithreading(query=my_query, pool_num=2, update_num=1000)
    # print(f'总耗时：{time.perf_counter() - start_time}')
    # 5线程，10条数据耗时2.3s
    # 5线程，100数据耗时2.7s
    # 5线程，1000数据耗时6.3s
    # 10线程，1000数据耗时6.5s
    # 20线程，1000数据耗时7.1s
    # 3线程，1000数据耗时5.7s
    # 2线程，1000数据耗时5.2s
    # 这多线程没啥用啊
    mlp_predict()
    print(f'总耗时：{time.perf_counter() - start_time}')
    # 使用bulk_update之后
    # 100数据，耗时0.035s
    # 1000数据，耗时0.248s
    # 10000数据，耗时2.506s
    # 100000数据，耗时23.929s
